package com.panopset.flywheel;

import static com.panopset.Util.*;

import java.io.StringWriter;
import java.util.Vector;

import com.panopset.StringLineSupplier;

/**
 * Once a Flywheel Script file is read, it becomes a template to be processed.
 * Templates may also be processed using the template directive within any
 * template.
 *
 * @author Karl Dinwiddie
 */
public class Template {

    /**
     * Every template is created by a Flywheel object.
     */
    public final Flywheel flywheel;

    /**
     * String line supplier.
     */
    public final StringLineSupplier sls;

    /**
     * Source file.
     */
    private SourceFile sourceFile;

    /**
     * First command.
     */
    Command firstCommand;

    /**
     * Last command.
     */
    Command lastCommand;

    /**
     * Current CommandFile for this template, used during execution. TODO: move
     * to method that uses it, doesn't belong here.
     */
    CommandFile commandFile;

    private Vector<Command> rawCommands;

    void exec(StringWriter sw) {
        for (Command topCommand : getTopCommands()) {
            if (flywheel.isStopped()) {
                return;
            }
            topCommand.resolve(sw);
            lastCommand = topCommand;
        }
    }

    private Vector<Command> getRawCommands() {
        if (rawCommands == null) {
            rawCommands = new RawCommandLoader(this).load();
            RawCommandLoader.addStructure(rawCommands);
        }
        return rawCommands;
    }

    private Vector<Command> topCommands;

    public Vector<Command> getTopCommands() {
        if (topCommands == null) {
            try {
                Vector<Command> commands = new ImpliedQuitFilter()
                        .addImpliedQuits(getRawCommands());
                topCommands = Util.matchQuitCommands(commands);
            } catch (Exception ex) {
                log(ex);
                stop(ex.getMessage());
            }
        }
        return topCommands;
    }

    /**
     * Output template results to a writer.
     * @param writer
     */
    public void output(StringWriter writer) {
        try {
            for (Command topCommand : getTopCommands()) {
                if (topCommand == null || flywheel == null
                        || flywheel.getWriter() == null) {
                    return;
                }
                topCommand.resolveCommand(flywheel.getWriter());
            }
        } catch (Throwable t) {
            log(t);
            stop(t.getMessage());
        }
    }

    /**
     * Called if something unexpected happened. Calls script.stop as well.
     *
     * @param msg
     */
    private void stop(String msg) {
        log(x("Stopped while processing line ") + sls.line + COLON
                + SINGLE_SPACE + sls.getFullName());
        flywheel.stop(msg);
    }

    public String getRelativePath() {
        if (sourceFile == null) {
            return "";
        } else {
            return sourceFile.relativePath;
        }
    }

    public Template(Flywheel flywheel, SourceFile sourceFile) {
        this.flywheel = flywheel;
        this.sourceFile = sourceFile;
        this.sls = new StringLineSupplier(sourceFile.file);
    }

    public Template(Flywheel flywheel, StringLineSupplier sls) {
        this.flywheel = flywheel;
        this.sourceFile = null;
        this.sls = sls;
    }

}
